home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 1 / QRZ Ham Radio Callsign Database - December 1993.iso / ucsd / packet / tcpip / amiga / asrc29k.lha / nntpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-08  |  31.2 KB  |  1,130 lines

  1. /*
  2.  *   Client routines for Network News Tranfer Protocol ala RFC977
  3.  *
  4.  *   Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
  5.  *   Permission granted for non-commercial copying and use, provided
  6.  *   this notice is retained.
  7.  *
  8.  *   Changes copyright 1990 Bernie Roehl, All Rights Reserved.
  9.  *   Permission granted for non-commercial copying and use, provided
  10.  *   this notice is retained.
  11.  *
  12.  *  Revision history:
  13.  *
  14.  *     May 11, 1990 - br checked for invalid chars in news filenames
  15.  *
  16.  *     May 10, 1990 - br changed date stamp in 'From ' lines to
  17.  *            seconds since GMT (to make parsing and expiry easier)
  18.  *
  19.  *     May 9, 1990 - br added locking of nntp.dat and history files,
  20.  *            second parameter to NNTP DIR, fixed bug in updating of
  21.  *            nntp.dat
  22.  *
  23.  *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
  24.  *            server-specific newsgroups and connection windows,
  25.  *            locking of newsgroup files using mlock() and rmlock(),
  26.  *            date stamping of 'From ' lines, increased stack space,
  27.  *            updating of nntp.dat only on successful sessions.
  28.  *
  29.  *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
  30.  *
  31.  *     July 12, 1992 G1YYH added POST, PROFILE, KILL, MESSAGES subcommands
  32.  */
  33. #include <stdio.h>
  34. #include <exec/types.h>
  35. #include <time.h>
  36. #include <ctype.h>
  37. #include <string.h>  /* for strchr() */
  38. #ifdef   __TURBOC__
  39. #include <dir.h>
  40. #endif
  41. #include "global.h"
  42. #include "timer.h"
  43. #include "cmdparse.h"
  44. #include "commands.h"
  45. #include "socket.h"
  46. #include "usock.h"
  47. #include "netuser.h"
  48. #include "proc.h"
  49. #include "smtp.h"
  50. #include "files.h"
  51. #include "session.h"
  52. #include "dirutil.h"
  53. #include "amiga/stat.h"
  54.  
  55. #define NNTPMAXLEN   512
  56.  
  57. static struct nntpservers {
  58.    struct timer nntpcli_t;
  59.    char *name;
  60.    char *groups;
  61.    int lowtime, hightime;  /* for connect window */
  62.    struct nntpservers *next;
  63. };
  64.  
  65. #define   NULLNNTP   (struct nntpservers *)NULL
  66.  
  67. #define MAXGROUPDIRS 10
  68.  
  69. static struct grouploc {
  70.    char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
  71.    char *directory;     /* directory where these groups should be */
  72.    } groupdirs[MAXGROUPDIRS] = { NULL, NULL };
  73.  
  74. struct nntpservers *Nntpservers = NULLNNTP;
  75. static char *Nntpgroups = NULLCHAR;
  76. static unsigned short nntptrace = 1;
  77.  
  78. static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  79.  
  80. static void nntptick __ARGS((void *tp));
  81. static void nntp_job __ARGS((int i1,void *tp,void *v1));
  82. static int gettxt __ARGS((int s,FILE *fp));
  83. static int getreply __ARGS((int s));
  84. static int getarticle __ARGS((int s,char *msgid));
  85. static int dogroups __ARGS((int argc,char *argv[],void *p));
  86. static int doadds __ARGS((int argc,char *argv[],void *p));
  87. static int dodrops __ARGS((int argc,char *argv[],void *p));
  88. static int dokicks __ARGS((int argc,char *argv[],void *p));
  89. static int dolists __ARGS((int argc,char *argv[],void *p));
  90. static int donntrace __ARGS((int argc,char *argv[],void *p));
  91. static int dondir __ARGS((int argc,char *argv[],void *p));
  92.  
  93. static int donnprofile __ARGS((int argc,char *argv[],void *p));
  94. static int donnfull __ARGS((int argc,char *argv[],void *p));
  95. static int donnhost __ARGS((int argc,char *argv[],void *p));
  96. static int donnorgan __ARGS((int argc,char *argv[],void *p));
  97. static int donnreply __ARGS((int argc,char *argv[],void *p));
  98. static int donnsig __ARGS((int argc,char *argv[],void *p));
  99. static int donnuser __ARGS((int argc,char *argv[],void *p));
  100. static int donnpost __ARGS((int argc,char *argv[],void *p));
  101. static int check_blank __ARGS((char *bp));
  102. static int donntplist __ARGS((int argc,char *argv[],void *p));
  103. static int donntpkill __ARGS((int argc,char *argv[],void *p));
  104.  
  105. static char *Puser     = NULLCHAR;
  106. static char *Preply    = NULLCHAR;
  107. static char *Psig      = NULLCHAR;
  108. static char *Porgan    = NULLCHAR;
  109. static char *Pfullname = NULLCHAR;
  110. static char *Host      = NULLCHAR;
  111.  
  112. /* Tracing levels:
  113.    0 - no tracing
  114.    1 - serious errors reported
  115.    2 - transient errors reported
  116.    3 - session progress reported
  117.    4 - actual received articles displayed
  118.  */
  119.  
  120. static struct cmds Nntpcmds[] = {
  121.    "addserver",   doadds,      0, 3, "nntp addserver <nntpserver> <interval>",
  122.    "directory",   dondir,      0, 0, NULLCHAR,
  123.    "dropserver",  dodrops,     0, 2, "nntp dropserver <nntpserver>",
  124.    "groups",      dogroups,    0, 0, NULLCHAR,
  125.    "kick",        dokicks,     0, 2, "nntp kick <nntpserver>",
  126.    "kill",        donntpkill,  0, 2, "kill <jobnumber>",
  127.    "listservers", dolists,     0, 0, NULLCHAR,
  128.    "messages",    donntplist,  0, 0, NULLCHAR,
  129.    "profile",     donnprofile, 0, 0, NULLCHAR,
  130.    "post",        donnpost,    2024, 0, NULLCHAR,
  131.    "trace",       donntrace,   0, 0, NULLCHAR,
  132.    NULLCHAR,
  133. };
  134.  
  135. int donntp(argc,argv,p)
  136. int argc;
  137. char *argv[];
  138. void *p;
  139. {
  140.    if(argc == 1)
  141.       return donntplist(argc,argv,p);
  142.    return subcmd(Nntpcmds,argc,argv,p);
  143. }
  144.  
  145. static int doadds(argc,argv,p)
  146. int argc;
  147. char *argv[];
  148. void *p;
  149. {
  150.    struct nntpservers *np;
  151.    for(np = Nntpservers; np != NULLNNTP; np = np->next)
  152.       if(stricmp(np->name,argv[1]) == 0)
  153.          break;
  154.    if (np == NULLNNTP) {
  155.       np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  156.       np->name = strdup(argv[1]);
  157.       np->next = Nntpservers;
  158.       Nntpservers = np;
  159.       np->groups = NULLCHAR;
  160.       np->lowtime = np->hightime = -1;
  161.       np->nntpcli_t.func = nntptick;   /* what to call on timeout */
  162.       np->nntpcli_t.arg = (void *)np;
  163.    }
  164.    if (argc > 3) {
  165.       int i;
  166.       if (np->groups == NULLCHAR) {
  167.          np->groups = mallocw(NNTPMAXLEN);
  168.          *np->groups = '\0';
  169.       }
  170.       for (i = 3; i < argc; ++i) {
  171.          if (isdigit(*argv[i])) {
  172.             int lh, ll, hh, hl;
  173.             sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  174.             np->lowtime = lh * 100 + ll;
  175.             np->hightime = hh * 100 + hl;
  176.          } else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
  177.             tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  178.          else {  /* it's a group, and it fits... add it to list */
  179.             if (*np->groups != '\0')
  180.                strcat(np->groups, ",");
  181.             strcat(np->groups, argv[i]);
  182.          }
  183.       }
  184.    }
  185.    /* set timer duration */
  186.    np->nntpcli_t.start = atol(argv[2])*(1000/MSPTICK);
  187.    start_timer(&np->nntpcli_t);      /* and fire it up */
  188.    return 0;
  189. }
  190.  
  191. static int dodrops(argc,argv,p)
  192. int argc;
  193. char *argv[];
  194. void *p;
  195. {
  196.    struct nntpservers *np, *npprev = NULLNNTP;
  197.    for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  198.       if(stricmp(np->name,argv[1]) == 0) {
  199.          stop_timer(&np->nntpcli_t);
  200.          free(np->name);
  201.          if (np->groups)
  202.             free(np->groups);
  203.          if(npprev != NULLNNTP)
  204.             npprev->next = np->next;
  205.          else
  206.             Nntpservers = np->next;
  207.          free((char *)np);
  208.          return 0;
  209.    }
  210.    tprintf("No such server enabled.\n");
  211.    return 0;
  212. }
  213.  
  214. static int dolists(argc,argv,p)
  215. int argc;
  216. char *argv[];
  217. void *p;
  218. {
  219.    struct nntpservers *np;
  220.    for(np = Nntpservers; np != NULLNNTP; np = np->next) {
  221.       char tbuf[80];
  222.       if (np->lowtime != -1 && np->hightime != -1)
  223.          sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  224.       else
  225.          tbuf[0] = '\0';
  226.       tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  227.          read_timer(&np->nntpcli_t) * MSPTICK/1000,
  228.          dur_timer(&np->nntpcli_t) * MSPTICK/1000,
  229.          tbuf, np->groups ? np->groups : "");
  230.    }
  231.    return 0;
  232. }
  233.  
  234. static int donntrace(argc, argv, p)
  235. int argc;
  236. char *argv[];
  237. void *p;
  238. {
  239.    return setshort(&nntptrace,"NNTP tracing",argc,argv);
  240. }
  241.    
  242. static char *News_spool = NULL;
  243. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  244.  
  245. static int dondir(argc, argv, p)
  246. int argc;
  247. char *argv[];
  248. void *p;
  249. {
  250.    if (argc < 2) {
  251.       int i;
  252.       tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  253.       tprintf("control: %s\n", Newsdir);
  254.       for (i = 0; i < MAXGROUPDIRS; ++i)
  255.          if (groupdirs[i].prefix)
  256.             tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
  257.    } else {
  258.       char *p;
  259.       if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
  260.          int i;
  261.          *p++ = '\0';
  262.          for (i = 0; i < MAXGROUPDIRS; ++i)
  263.             if (groupdirs[i].prefix)
  264.                if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
  265.                   if (groupdirs[i].directory) {
  266.                      free(groupdirs[i].directory);
  267.                      groupdirs[i].directory = NULLCHAR;
  268.                      }
  269.                   if (*p == '\0') {
  270.                      free(groupdirs[i].prefix);
  271.                      groupdirs[i].prefix = NULLCHAR;
  272.                   } else
  273.                      groupdirs[i].directory = strdup(p);
  274.                   return 0;
  275.                }
  276.          if (*p == '\0')  /* trashing a group that's not there */
  277.             return 0;
  278.          for (i = 0; i < MAXGROUPDIRS; ++i){
  279.             if (groupdirs[i].prefix == NULLCHAR) {
  280.                groupdirs[i].prefix = strdup(argv[1]);
  281.                if (groupdirs[i].directory) {
  282.                   free(groupdirs[i].directory);
  283.                   groupdirs[i].directory = NULL;
  284.                }
  285.                groupdirs[i].directory = strdup(p);
  286.                return 0;
  287.             }
  288.          }
  289.          tprintf("Directory table full\n");
  290.       }
  291.       else {  /* no '=', so just set default */
  292.          if (News_spool)
  293.             free(News_spool);
  294.          News_spool = strdup(argv[1]);
  295.       }
  296.       if (argc > 2) {  /* they specified a newsdir as well */
  297.          if (np_all)
  298.             free(Newsdir);
  299.          Newsdir = strdup(argv[2]);
  300.          np_all = 1;
  301.       }
  302.    }
  303.    return 0;
  304. }
  305.    
  306. static int dokicks(argc,argv,p)
  307. int argc;
  308. char *argv[];
  309. void *p;
  310. {
  311.    struct nntpservers *np;
  312.    for(np = Nntpservers; np != NULLNNTP; np = np->next)
  313.       if(stricmp(np->name,argv[1]) == 0) {
  314.          /* If the timer is not running, the timeout function has
  315.          * already been called and we don't want to call it again.
  316.          */
  317.          if(run_timer(&np->nntpcli_t)) {
  318.             stop_timer(&np->nntpcli_t);
  319.             nntptick((void *)np);
  320.          }
  321.          return 0;
  322.    }
  323.    tprintf("No such server enabled.\n");
  324.    return 0;
  325. }
  326.  
  327. static int dogroups(argc,argv,p)
  328. int argc;
  329. char *argv[];
  330. void *p;
  331. {
  332.    int i;
  333.    if(argc < 2) {
  334.       if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  335.          tprintf("All groups are currently enabled.\n");
  336.       else
  337.          tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  338.       return 0;
  339.    }
  340.    if(Nntpgroups == NULLCHAR)
  341.       Nntpgroups = mallocw(NNTPMAXLEN);
  342.    *Nntpgroups = '\0';
  343.    for(i=1; i < argc; ++i) {
  344.       if(i > 1)
  345.          strcat(Nntpgroups,",");
  346.       strcat(Nntpgroups,argv[i]);
  347.    }
  348.    return 0;
  349. }
  350.  
  351. /* This is the routine that gets called every so often to connect to
  352.  * NNTP servers.
  353.  */
  354. static void nntptick(tp)
  355. void *tp;
  356. {
  357.    newproc("NNTP client", 3072, nntp_job, 0, tp, NULL);
  358. }
  359.  
  360. static void nntp_job(i1,tp,v1)
  361. int i1;
  362. void *tp, *v1;
  363. {
  364.    FILE *fp, *tmpf, *wfile, *tfile;
  365.    int s, i;
  366. /*   long pos; */
  367.    struct tm *ltm;
  368.    time_t t;
  369.    int now;
  370.    struct nntpservers *np = (struct nntpservers *) tp;
  371.    struct sockaddr_in fsocket;
  372.    char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *cp1,
  373.       *lastdate = NULLCHAR, wfilename[13], nnfix[9],
  374.       wtmp[LINELEN], ttmp[LINELEN], wgroup[80], wmsgid[80];
  375.  
  376.    if (nntptrace >= 2)
  377.       printf("NNTP Checking NewsGroups on \"%s\"\n",np->name);
  378.    if(availmem() < Memthresh){
  379.       if (nntptrace >= 2)
  380.          printf("NNTP daemon quit -- low memory\n");
  381.       /* Memory is tight, don't do anything */
  382.       start_timer(&np->nntpcli_t);
  383.       return;
  384.    }
  385.  
  386.    time(&t);   /* more portable than gettime() */
  387.    ltm = localtime(&t);
  388.    now = ltm->tm_hour * 100 + ltm->tm_min;
  389.    if (np->lowtime < np->hightime) {  /* doesn't cross midnight */
  390.       if (now < np->lowtime || now >= np->hightime) {
  391.          if (nntptrace >= 3)
  392.             printf("NNTP window to '%s' not open\n", np->name);
  393.          start_timer(&np->nntpcli_t);
  394.          return;
  395.       }
  396.    } else {
  397.       if (now < np->lowtime && now >= np->hightime) {
  398.          if (nntptrace >= 3)
  399.             printf("NNTP window to '%s' not open\n", np->name);
  400.          start_timer(&np->nntpcli_t);
  401.          return;
  402.       }
  403.    }
  404.  
  405.    fsocket.sin_addr.s_addr = resolve(np->name);
  406.    if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
  407.       if (nntptrace >= 2)
  408.          printf("NNTP can't resolve host '%s'\n", np->name);
  409.       /* Try again later */
  410.       start_timer(&np->nntpcli_t);
  411.       return;
  412.    }
  413.    fsocket.sin_family = AF_INET;
  414.    fsocket.sin_port = IPPORT_NNTP; 
  415.  
  416.    s = socket(AF_INET,SOCK_STREAM,0);
  417.    /* sockmode(s,SOCK_ASCII); */
  418.    if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
  419.       cp = sockerr(s);
  420.       mainlog(s,"NNTP %s Connect failed: %s",psocket(&fsocket),
  421.          cp != NULLCHAR ? cp : "");
  422.       if (nntptrace >= 2)
  423.          printf("NNTP %s Connect failed: %s\n",psocket(&fsocket),
  424.       cp != NULLCHAR ? cp : "");
  425.       goto quit;
  426.    }
  427.    /* Eat the banner */
  428.    i = getreply(s);
  429.    if(i >= 400) {
  430.       mainlog(s,"NNTP %s bad reply on banner (response was %d)",psocket(&fsocket),i);
  431.       if (nntptrace >= 1)
  432.          printf("NNTP %s bad reply on banner (response was %d)\n",psocket(&fsocket),i);
  433.       goto quit;
  434.    }
  435.  
  436. if(i == 201) {
  437.    if (nntptrace >= 1)
  438.       printf("NNTP - No posting allowed on '%s'\n", np->name);
  439. } else {
  440.    for(filedir(Newsqueue,0,wfilename); wfilename[0] != '\0';
  441.       filedir(Newsqueue,1,wfilename)) {
  442.  
  443.       cp = wfilename;
  444.       cp1 = nnfix;
  445.       while (*cp && *cp != '.')
  446.          *cp1++ = *cp++;
  447.       *cp1 = '\0';
  448.  
  449.       sprintf(wtmp,"%s/%s",Newsdir,wfilename);
  450.       if ((wfile = fopen(wtmp,READ_TEXT)) == NULLFILE) 
  451.          continue;
  452.       (void) fgets(wgroup,LINELEN,wfile);      /* read target group */
  453.       rip(wgroup);
  454.       (void) fgets(wmsgid,LINELEN,wfile);      /* read messageid */
  455.       rip(wmsgid);
  456.       fclose(wfile);
  457.  
  458.       if (nntptrace >= 3)
  459.          printf("==>GROUP %s\n", wgroup);
  460.       usprintf(s,"GROUP %s\n", wgroup);
  461.       i = getreply(s);
  462.       if(i == 411) {
  463.          if (nntptrace >= 1)
  464.             printf("NNTP No Such Newsgroup '%s'\n",wgroup);
  465.          continue;
  466.       }
  467.       if(i == 211) {
  468.          if (nntptrace >= 3)
  469.             printf("==>IHAVE %s\n", wmsgid);
  470.          usprintf(s,"IHAVE %s\n", wmsgid);
  471.          i = getreply(s);
  472.          if(i == 335) {
  473.             /* Send It */
  474.             sprintf(ttmp,"%s/%s.txt", Newsdir, nnfix);
  475.             if((tfile = fopen(ttmp, READ_TEXT)) != NULLFILE) {
  476.                while(fgets(ttmp, LINELEN, tfile) != NULLCHAR)
  477.                   usprintf(s,ttmp);
  478.                fclose(tfile);
  479.             }
  480.             i = getreply(s);
  481.             if(i == 235)
  482.                remove(wtmp);
  483.          }
  484.          if(i == 436)
  485.             if (nntptrace >= 1)
  486.                printf("NNTP '%s' Transfer Failed - Try later\n", wmsgid);
  487.          if(i == 435) {
  488.             if (nntptrace >= 1)
  489.                printf("NNTP Article '%s' Not wanted\n", wmsgid);
  490.             remove(wtmp);
  491.          }
  492.          if(i == 437) {
  493.             if (nntptrace >= 1)
  494.                printf("NNTP Article '%s' Rejected\n", wmsgid);
  495.             remove(wtmp);
  496.          }
  497.       }
  498.    }
  499. }
  500.  
  501.    if (mlock(Newsdir, "nntp")) {
  502.       if (nntptrace >= 2)
  503.          printf("NNTP %s Connect failed: cannot lock nntp.dat\n", psocket(&fsocket));
  504.       goto quit;
  505.    }
  506.    sprintf(buf,"%s/nntp.dat",Newsdir);
  507.    if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  508.       mainlog(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket),
  509.          buf);
  510.       if (nntptrace >= 1)
  511.          printf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  512.       rmlock(Newsdir, "nntp");
  513.       goto quit;
  514.    }
  515.    rewind(fp);
  516. /*   for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
  517.    for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
  518.       if((cp = strchr(buf,' ')) == NULLCHAR)
  519.          continue;   /* something wrong with this line, skip it */
  520.       *cp = '\0';
  521.       if(stricmp(buf,np->name) == 0) {
  522.          rip(cp+1);
  523.          lastdate = strdup(cp+1);
  524.          break;
  525.       }
  526.    }
  527.    fclose(fp);
  528.    rmlock(Newsdir, "nntp");
  529.  
  530.    if(lastdate == NULLCHAR)
  531.       lastdate = strdup("700101 000000");
  532.    /* snapshot the time for use later in re-writing nntp.dat */
  533.    time(&t);
  534.    ltm = localtime(&t);
  535.             
  536.    /* Get a list of new message-id's */
  537.    if (np->groups) {
  538.       if (nntptrace >= 3)
  539.          printf("==>NEWNEWS %s %s\n", np->groups, lastdate);
  540.       usprintf(s,"NEWNEWS %s %s\n", np->groups, lastdate);
  541.    } else {
  542.       if (nntptrace >= 3)
  543.          printf("==>NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  544.       usprintf(s,"NEWNEWS %s %s\n",Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  545.    }
  546.    free(lastdate);
  547.    /* Get the response */
  548.    if((i = getreply(s)) != 230) { /* protocol error */
  549.       mainlog(s,"NNTP %s protocol error (response was %d)",psocket(&fsocket),i);
  550.       if (nntptrace >= 1)
  551.          printf("NNTP %s protocol error (response was %d)\n",psocket(&fsocket),i);
  552.       goto quit;
  553.    }
  554.    if((tmpf = tmpfile()) == NULLFILE) {
  555.       if (nntptrace >= 1)
  556.          printf("NNTP %s Cannot open temp file\n", psocket(&fsocket));
  557.       goto quit;
  558.    }
  559.    if(gettxt(s,tmpf) == -1) {
  560.       mainlog(s,"NNTP %s giving up: gettxt() failure",psocket(&fsocket));
  561.       if (nntptrace >= 1)
  562.          printf("NNTP %s giving up: gettxt() failure\n",psocket(&fsocket));
  563.       fclose(tmpf);
  564.       goto quit;
  565.    }
  566.  
  567.    /* Open the history file */
  568.    if (mlock(Newsdir, "history")) {
  569.       if (nntptrace >= 1)
  570.          printf("NNTP %s giving up: couldn't lock history file\n", psocket(&fsocket));
  571.       fclose(tmpf);
  572.       goto quit;
  573.    }
  574.    sprintf(buf,"%s/history",Newsdir);
  575.    if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  576.       mainlog(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket), buf);
  577.       if (nntptrace >= 1)
  578.          printf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  579.       fclose(tmpf);
  580.       goto quit;
  581.    }
  582.    /* search through the history file for matching message id's */
  583.    rewind(tmpf);
  584.    while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  585.       i = 0;
  586.       rewind(fp);
  587.       while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
  588.          if(stricmp(buf,tbuf) == 0) {
  589.             i = 1;
  590.             break;
  591.          }
  592.          pwait(NULL);
  593.       }
  594.       if(i == 0) {      /* not found, get the article */
  595.          if(getarticle(s,tbuf) == -1) {
  596.             mainlog(s,"NNTP %s Giving up: could not get article",psocket(&fsocket));
  597.             if (nntptrace >= 2)
  598.                printf("NNTP %s Giving up: could not get article\n",psocket(&fsocket));
  599.             fclose(fp);
  600.             rmlock(Newsdir, "history");
  601.             fclose(tmpf);
  602.             goto quit;
  603.          }
  604.          fprintf(fp,"%s",tbuf); /* add the new message id */
  605.       }
  606.    }
  607.    fclose(fp);
  608.    rmlock(Newsdir, "history");
  609.    fclose(tmpf);
  610.    if (nntptrace >= 3)
  611.       printf("==>QUIT\n");
  612.    usprintf(s,"QUIT\n");
  613.    /* Eat the response */
  614.    getreply(s);
  615.    /* NOW, update the nntp.dat file */
  616.    if (mlock(Newsdir, "nntp")) {
  617.       if (nntptrace >= 2)
  618.          printf("NNTP %s Could not lock nntp.dat for update\n", psocket(&fsocket));
  619.       goto quit;
  620.    }
  621.    sprintf(buf,"%s/nntp.dat",Newsdir);
  622.    fp = fopen(buf,READ_TEXT);
  623.    sprintf(buf, "%s/nntp.tmp",Newsdir);
  624.    if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
  625.       if (nntptrace >= 1)
  626.          printf("NNTP %s Cannot create temp file '%s'\n", psocket(&fsocket), buf);
  627.    if (fp == NULLFILE || tmpf == NULLFILE) {
  628.       mainlog(s,"NNTP %s Could not update %s", psocket(&fsocket), buf);
  629.       if (nntptrace >= 2)
  630.          printf("NNTP %s Could not update %s\n",psocket(&fsocket), buf);
  631.       if (fp)
  632.          fclose(fp);
  633.       if (tmpf)
  634.          fclose(tmpf);
  635.       rmlock(Newsdir, "nntp");
  636.       goto quit;
  637.    }
  638.    while (fgets(tbuf, sizeof(tbuf), fp))
  639.       if (strnicmp(tbuf, np->name, strlen(np->name)))
  640.          fputs(tbuf, tmpf);
  641.    fprintf(tmpf,"%s %02d%02d%02d %02d%02d%02d\n",np->name,ltm->tm_year%100,ltm->tm_mon+1,
  642.       ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  643.    fclose(fp);
  644.    fclose(tmpf);
  645.    sprintf(buf, "%s/nntp.dat", Newsdir);
  646.    sprintf(tbuf, "%s/nntp.tmp", Newsdir);
  647.    remove(buf);
  648.    rename(tbuf, buf);
  649.    rmlock(Newsdir, "nntp");
  650. quit:
  651.    if (nntptrace >= 2)
  652.       printf("NNTP Finished with \"%s\"\n", np->name);
  653.    close_s(s);
  654.    /* Restart timer */
  655.    start_timer(&np->nntpcli_t);
  656.    return;
  657. }
  658.  
  659. static int gettxt(s,fp)
  660. int s;
  661. FILE *fp;
  662. {
  663.    char buf[NNTPMAXLEN];
  664.    int nlines;
  665.    for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
  666.       if (nntptrace >= 4)
  667.          printf("<==%s", buf);
  668.       if(strcmp(buf,".\n") == 0) {
  669.          if (nntptrace >= 3)
  670.             printf("NNTP received %d lines\n", nlines);
  671.          return 0;
  672.          }
  673.       /* check for escaped '.' characters */
  674.       if(strcmp(buf,"..\n") == 0)
  675.          fputs(".\n",fp);
  676.       else
  677.          fputs(buf,fp);
  678.    }
  679.    if (nntptrace >= 1)
  680.       printf("NNTP receive error after %d lines\n", nlines);
  681.    return -1;
  682. }
  683.  
  684. static int getreply(s)
  685. int s;
  686. {
  687.    char buf[NNTPMAXLEN];
  688.    int response;
  689.    while(recvline(s,buf,NNTPMAXLEN) != -1) {
  690.       /* skip informative messages and blank lines */
  691.       if(buf[0] == '\0' || buf[0] == '1' || buf[0] == '5')
  692.          continue;
  693.       sscanf(buf,"%d",&response);
  694.       if (nntptrace >= 3)
  695.          printf("<==%s\n", buf);
  696.       return response;
  697.    }
  698.    if (nntptrace >= 3)
  699.       printf("==No response\n");
  700.    return -1;
  701. }
  702.  
  703. static int getarticle(s,msgid)
  704. int s;
  705. char *msgid;
  706. {
  707.    char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
  708.    FILE *fp, *tmpf;
  709.    int r;
  710.    char *cp;
  711.  
  712.    if (nntptrace >= 3)
  713.       printf("==>ARTICLE %s", msgid);
  714.    usprintf(s,"ARTICLE %s", msgid);
  715.    r = getreply(s);
  716.    if(r == -1 || r >= 500)
  717.       return -1;
  718.    if(r >= 400)
  719.       return 0;
  720.    if((tmpf = tmpfile()) == NULLFILE) {
  721.       if (nntptrace >= 1)
  722.          printf("NNTP Cannot open temp file for article\n");
  723.       return -1;
  724.    }
  725.    if(gettxt(s,tmpf) == -1) {
  726.       fclose(tmpf);
  727.       return -1;
  728.    }
  729.    /* convert the article into mail format */
  730.    rewind(tmpf);
  731.    froml[0] = '\0';
  732.    newgl[0] = '\0';
  733.    while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  734.       if(strncmp(buf,"From: ",6) == 0) {
  735.          time_t t;
  736.          rip(&buf[6]);
  737.          time(&t);
  738.          sprintf(froml,"From %s %s",&buf[6], ctime(&t));
  739.          if(newgl[0] != '\0')
  740.             break;
  741.       }
  742.       if(strncmp(buf,"Newsgroups: ",12) == 0) {
  743.          strcpy(newgl,&buf[12]);
  744.          if(froml[0] != '\0')
  745.             break;
  746.       }
  747.       /* invalid article - missing 'From:' line or 'Newsgroups:' line */
  748.       if(strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
  749. /*         fclose(fp); */
  750.          fclose(tmpf);
  751.          return 0;
  752.       }
  753.    }
  754.    sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  755.    for(cp=newgl;;++cp) {
  756.       if(*cp == '.') {
  757.          mkdir(buf); /* create a subdirectory, if necessary */
  758.          strcat(buf,"/");
  759.          continue;
  760.       }
  761.       if(*cp == ',' || *cp == '\n') {
  762.          char tempdir[80], prefix[20], *p;
  763.          strcpy(tempdir, buf);
  764.          if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
  765.             *p++ = '\0';
  766.             strcpy(prefix, p);
  767.          }
  768.          if (mlock(tempdir, prefix)) {
  769.             if (nntptrace >= 2)
  770.                printf("NNTP group '%s' is locked\n", buf);
  771.             return -1;
  772.          }
  773.          strcat(buf,".txt");
  774.          /* open the mail file */
  775.          if (nntptrace >= 2)
  776.             printf("Writing article to '%s'\n", buf);
  777.          if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
  778.             fputs(froml,fp);
  779.             rewind(tmpf);
  780.             while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  781.                /* for UNIX mail compatiblity */
  782.                if(strncmp(buf,"From ",5) == 0)
  783.                   putc('>',fp);
  784.                fputs(buf,fp);
  785.             }
  786.             putc('\n',fp);
  787.             fclose(fp);
  788.          }
  789.          rmlock(tempdir, prefix);
  790.          if (*cp == '\n') 
  791.             break;
  792.          else
  793.             sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  794.          continue;
  795.       }
  796.       buf[strlen(buf)+1] = '\0';
  797.       buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
  798.    }
  799.    fclose(tmpf);
  800.    return 0;
  801. }
  802.  
  803. /* -------------------- Profile subcmds -------------------- */
  804.  
  805. static int donnuser(argc,argv,p)
  806. int argc;
  807. char *argv[];
  808. void *p;
  809. {
  810.    if(argc < 2)
  811.       tprintf("%s\n",Puser);
  812.    else {
  813.       free(Puser);
  814.       Puser = strdup(argv[1]);
  815.    }
  816.    return 0;
  817. }
  818.  
  819. static int donnsig(argc,argv,p)
  820. int argc;
  821. char *argv[];
  822. void *p;
  823. {
  824.    if(argc < 2)
  825.       tprintf("%s\n",Psig);
  826.    else {
  827.       if(access(argv[1],0) == 0) {
  828.          free(Psig);
  829.          Psig = strdup(argv[1]);
  830.       } else {
  831.          tputs("No such signature file\n");
  832.          return -1;
  833.       }
  834.    }
  835.    return 0;
  836. }
  837.  
  838. static int donnfull(argc,argv,p)
  839. int argc;
  840. char *argv[];
  841. void *p;
  842. {
  843.    if(argc < 2)
  844.       tprintf("%s\n",Pfullname);
  845.    else {
  846.       free(Pfullname);
  847.       Pfullname = strdup(argv[1]);
  848.    }
  849.    return 0;
  850. }
  851.  
  852. static int donnhost(argc,argv,p)
  853. int argc;
  854. char *argv[];
  855. void *p;
  856. {
  857.    if(argc < 2)
  858.       tprintf("%s\n",Host);
  859.    else {
  860.       free(Host);
  861.       Host = strdup(argv[1]);
  862.    }
  863.    return 0;
  864. }
  865.  
  866. static int donnorgan(argc,argv,p)
  867. int argc;
  868. char *argv[];
  869. void *p;
  870. {
  871.    if(argc < 2)
  872.       tprintf("%s\n",Porgan);
  873.    else {
  874.       free(Porgan);
  875.       Porgan = strdup(argv[1]);
  876.    }
  877.    return 0;
  878. }
  879.  
  880. static int donnreply(argc,argv,p)
  881. int argc;
  882. char *argv[];
  883. void *p;
  884. {
  885.    if(argc < 2)
  886.       tprintf("%s\n",Preply);
  887.    else {
  888.       free(Preply);
  889.       Preply = strdup(argv[1]);
  890.    }
  891.    return 0;
  892. }
  893.  
  894. struct cmds Prof[] = {
  895.    "fullname",   donnfull,    0, 0, NULLCHAR,
  896.    "host",       donnhost,    0, 0, NULLCHAR,
  897.    "organ",      donnorgan,   0, 0, NULLCHAR,
  898.    "reply",      donnreply,   0, 0, NULLCHAR,
  899.    "sig",        donnsig,     0, 0, NULLCHAR,
  900.    "user",       donnuser,    0, 0, NULLCHAR,
  901.    NULLCHAR,
  902. };
  903.  
  904. /* subcmd parser */
  905. static int donnprofile(argc,argv,p)
  906. int argc;
  907. char *argv[];
  908. void *p;
  909. {
  910.    if(argc == 1) {
  911.       if (Host == NULLCHAR) {
  912.          Host = strdup(Hostname);
  913.       }
  914.  
  915.       if (Host != NULLCHAR)
  916.          tprintf("Hostname     : %s\n",Host);
  917.       if (Pfullname != NULLCHAR)
  918.          tprintf("Full Name    : %s\n",Pfullname);
  919.       if (Porgan != NULLCHAR)
  920.          tprintf("Organisation : %s\n",Porgan);
  921.       if (Preply != NULLCHAR)
  922.          tprintf("Reply-To     : %s\n",Preply);
  923.       if (Psig != NULLCHAR)
  924.          tprintf("Signature    : %s\n",Psig);
  925.       if (Puser != NULLCHAR)
  926.          tprintf("User Name    : %s\n",Puser);
  927.  
  928.       return 0;
  929.    }
  930.    return subcmd(Prof,argc,argv,p);
  931. }
  932.  
  933. /* manually entering new news
  934.  * returncode: -1 if error; 0 success */
  935. static int donnpost(argc,argv,p)
  936. int argc;
  937. char *argv[];
  938. void *p;
  939. {
  940.    struct session *sp;
  941.    char buf[NNTPMAXLEN], pbuf[80], qbuf[80];
  942.    int id;
  943.    long currtime;
  944.    FILE *idf, *ufp, *fp, *fq;
  945.  
  946.    if((sp = newsession("Post",POST)) == NULLSESSION) {
  947.       tprintf("Too many sessions\n");
  948.       freeargs(argc,argv);
  949.       return 1;
  950.    }
  951.  
  952.    for (;;) {
  953.       id = get_msgid();
  954.  
  955.       sprintf(pbuf,"%s/%d.txt",Newsdir,id);
  956.       fp = fopen(pbuf,WRITE_TEXT);
  957.       sprintf(qbuf,"%s/%d.wrk",Newsdir,id);
  958.       fq = fopen(qbuf,WRITE_TEXT);
  959.  
  960.       if (Puser == NULLCHAR) {
  961.          tprintf("User name? ");
  962.          recvline(sp->input,buf,NNTPMAXLEN);
  963.          rip(buf);
  964.          if (check_blank(buf)) {
  965.             fclose(fp);
  966.             fclose(fq);
  967.             remove(pbuf);
  968.             remove(qbuf);
  969.             goto done;
  970.          }
  971.          Puser = strdup(buf);
  972.       }
  973.  
  974.       fprintf(fp,"Path: %s\n",Host);
  975.       fprintf(fp,"From: %s@%s",Puser,Hostname);
  976.  
  977.       if (Pfullname != NULLCHAR)
  978.          fprintf(fp," (%s )",Pfullname);
  979.       fprintf(fp,"\n");
  980.  
  981.       tprintf("Newsgroup? ");
  982.       recvline(sp->input,buf,NNTPMAXLEN);
  983.       rip(buf);
  984.       if (check_blank(buf)) {
  985.          fclose(fp);
  986.          fclose(fq);
  987.          remove(pbuf);
  988.          remove(qbuf);
  989.          goto done;
  990.       }
  991.       fprintf(fp,"Newsgroups: %s\n",buf);
  992.       fprintf(fq,"%s\n",buf);
  993.  
  994.       tprintf("Subject? ");
  995.       recvline(sp->input,buf,NNTPMAXLEN);
  996.       rip(buf);
  997.       if (!check_blank(buf))
  998.          fprintf(fp,"Subject: %s\n",buf);
  999.       fprintf(fp,"Message-Id: <%d@%s>\n",id,Hostname);
  1000.       fprintf(fq,"<%d@%s>\n",id,Hostname);
  1001.       time(&currtime);
  1002.       fprintf(fp,"Date: %s",ptime(&currtime));
  1003.       fprintf(fp,"Sender: NNTP@%s\n",Hostname);
  1004.  
  1005.       if (Preply != NULLCHAR)
  1006.          fprintf(fp,"Reply-To: %s\n",Preply);
  1007.  
  1008.       if (Porgan != NULLCHAR)
  1009.          fprintf(fp,"Organization: %s\n",Porgan);
  1010.  
  1011.       fprintf(fp, "Comment: AmigaNOS v%s\n", Version);
  1012.  
  1013.       fprintf(fp,"\n");
  1014.       tputs("Enter message - end with . or /EX ('.r' or '.u' to upload)\n");
  1015.  
  1016.       for (;;) {
  1017.          recvline(sp->input,buf,NNTPMAXLEN);
  1018.          if(strcmp(buf,".u\n") == 0 || strcmp(buf,".r\n") == 0) {
  1019.             tputs("Filename? ");
  1020.             recvline(sp->input, buf, LINELEN);
  1021.             rip(buf);
  1022.             if((ufp = fopen(buf, READ_TEXT)) != NULLFILE) {
  1023.                while(fgets(buf, NNTPMAXLEN, ufp) != NULL)
  1024.                   fputs(buf, fp);
  1025.                fclose(ufp);
  1026.             }
  1027.             tputs("(continue)\n");
  1028.          }
  1029.          if(strcmp(buf,".\n") == 0 || 
  1030.             strcmpi(buf,"***END\n") == 0 || 
  1031.             strcmpi(buf,"/EX\n") == 0)
  1032.             break;
  1033.          fprintf(fp,"%s",buf);
  1034.       }
  1035.  
  1036.       if (Psig != NULLCHAR) {
  1037.          sprintf(buf,"%s",Psig);
  1038.          if ((idf = fopen(buf,READ_TEXT)) != NULLFILE ) {
  1039.             while(fgets(buf,NNTPMAXLEN,idf) != NULL)
  1040.                fprintf(fp,"%s",buf);
  1041.             fclose(idf);
  1042.          }
  1043.       }
  1044.  
  1045. loop:
  1046.       fprintf(fp,"\n.\n");
  1047.  
  1048.       fclose(fp);
  1049.       fclose(fq);
  1050.  
  1051.       tprintf("Post another? ");
  1052.       recvline(sp->input,buf,NNTPMAXLEN);
  1053.       if (tolower(buf[0]) == 'n')
  1054.          goto done;
  1055.    }
  1056.  
  1057. done:
  1058.    freesession(sp);
  1059.    return 0;
  1060. }
  1061.  
  1062. /* checks for not valid chars in a line
  1063.  * returncode: 0 if valid; 1 if invalid */
  1064. static int check_blank(bp)
  1065. char *bp;
  1066. {
  1067.    if (strpbrk(bp,"!@#$%^&*()_+=<>,./?~`[]{}\|0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") == NULL)
  1068.       return 1;
  1069.    return 0;
  1070. }
  1071.  
  1072. /* list messages wating to be sent */
  1073. static int donntplist(argc,argv,p)
  1074. int argc;
  1075. char *argv[];
  1076. void *p;
  1077. {
  1078.    char tstring[80], line[20],
  1079.         group[LINELEN], *cp;
  1080.    struct stat stbuf;
  1081.    struct tm *tminfo, *localtime();
  1082.    FILE *fp;
  1083.  
  1084.    Current->flowmode = 1;          /* Enable the more mechanism */
  1085.    tprintf("    Job    Size  Date  Time Group\n");
  1086.    filedir(Newsqueue,0,line);
  1087.    while(line[0] != '\0') {
  1088.       sprintf(tstring,"%s/%s",Newsdir,line);
  1089.       if ((fp = fopen(tstring,READ_TEXT)) == NULLFILE) {
  1090.          tprintf("Can't open %s: %s\n",tstring,sys_errlist[errno]);
  1091.          continue;
  1092.       }
  1093.       if ((cp = strrchr(line,'.')) != NULLCHAR)
  1094.          *cp = '\0';
  1095.       sprintf(tstring,"%s/%s.txt",Newsdir,line);
  1096.       stat(tstring,&stbuf);
  1097.       tminfo = localtime(&stbuf.st_ctime);
  1098.       fgets(group,sizeof(group),fp);
  1099.       rip(group);
  1100.       tprintf("%7s %7ld %02d/%02d %02d:%02d %-25s\n",
  1101.          line, stbuf.st_size,
  1102.          tminfo->tm_mon+1, tminfo->tm_mday, tminfo->tm_hour,
  1103.          tminfo->tm_min, group);
  1104.       (void) fclose(fp);
  1105.       pwait(NULL);
  1106.       filedir(Newsqueue,1,line);
  1107.    }
  1108.    Current->flowmode = 0;
  1109.    return 0;
  1110. }
  1111.  
  1112. /* kill a job in the mqueue */
  1113. static int donntpkill(argc,argv,p)
  1114. int argc;
  1115. char *argv[];
  1116. void *p;
  1117. {
  1118.    char s[LINELEN];
  1119.    char *cp;
  1120.  
  1121.    sprintf(s,"%s/%s.wrk",Newsdir,argv[1]);
  1122.    cp = strrchr(s,'.');
  1123.    if (remove(s))
  1124.       tprintf("Job id %s not found\n",argv[1]);
  1125.    strcpy(cp,".txt");
  1126.    (void) remove(s);
  1127.    return 0;
  1128. }
  1129.  
  1130.